package LongDivision;

import java.util.Arrays;
import java.util.Scanner;


/**
 * Деление в столбик
 *
 * @author Artur Dzidzoiev
 * @version 1.1.4 08 Oct 2013
 */
public class LongDivision {
    static final int BASE = 10;

    /**
     * Считывание данных с клавиатуры, вызов фунции деления, вывод
     * результатов на экран.
     */
    public static void main(String[] args) {
        int[] inputArr = getValues();
        int dividend = inputArr[0];
        int divisor = inputArr[1];
        String sign = checkSign(dividend,divisor) ? "-" : "";
        String divisionProcessStr = performDivision(dividend, divisor);
        System.out.print(divisionProcessStr);
        System.out.println(String.format("Remainder: %s%d", sign, dividend % divisor));
        System.out.println(String.format("Quotient (result): %s%d", sign, dividend / divisor));
    }

    /**
     * Метод, в котором в цикле вызываются метод шага деления и
     * форматирование строки промежуточных результатрв
     *
     * @return Строка процесса деления
     */
    public static String performDivision(int dividend, int divisor) {
        /*
        Нам для деления в столбик нужно 2 числа - делимое и вычетаемое
        Их ищем отдельно и форматируем строку с ними
         */

        int newDividend = dividend;
        StringBuilder divisionProcess = new StringBuilder();
        int indent = 1;

        do {
            int stepDividend = getStepDividendValue(newDividend, divisor);
            int subtractive = calculateSubtractive(stepDividend, divisor);
            newDividend = calculateNewDividend(stepDividend, newDividend, subtractive);
            divisionProcess.append(formatStep(indent, stepDividend, subtractive));
            indent += calculateResultIndent(stepDividend, subtractive);
        }
        while (newDividend >= divisor);
        return updateInitialStr(divisionProcess, dividend, divisor);
    }

    /**
     * Форматирование строки для одного шага деления
     *
     * @param indent - Значение сдвига текущего шага
     * @param stepDividend - Делимое на текущем шаге
     * @param subtractive - Вычеатемое на текущем шаге
     */
    private static String formatStep(int indent, int stepDividend, int subtractive) {
        StringBuilder stepStr = new StringBuilder();

        stepStr.append(generateSpaces(indent));
        stepStr.append(Integer.toString(stepDividend));
        stepStr.append("\n");
        stepStr.append(generateSpaces(indent - 1));
        stepStr.append(generateSpaces(calculateCurrentIndent(stepDividend, subtractive)));
        stepStr.append("-");
        stepStr.append(subtractive);
        stepStr.append("\n");
        return stepStr.toString();
    }

    /**
     * Замена первой строчки на начальное условие деления (тип dividend|divisor)
     */
    private static String updateInitialStr(StringBuilder divisionProcess, int dividend, int divisor) {
        int index = divisionProcess.indexOf("\n");

        return divisionProcess.replace(0, index, String.format(" %d|%d", dividend, divisor)).toString();
    }

    /**
     * Определение отступа перед результатом вычетания
     * после шага т.е. сдвиг на следующий шаг
     */
    private static int calculateResultIndent(int stepDividend, int subtractive) {
        int resultDifference = stepDividend - subtractive;
        String resultDifferenceStr = (resultDifference == 0) ? "" : Integer.toString(resultDifference);
        int length = resultDifferenceStr.length();
        int shift = 0;
        for (int i = length; i < Integer.toString(stepDividend).length(); i++) {
            shift++;
        }
        return shift;
    }

    /**
     * Определение отступа перед вычетаемым (на текущем шаге)
     */
    private static int calculateCurrentIndent(int stepDividend, int subtractive) {
        int shift;
        shift = 0;
        while (shift < (Integer.toString(stepDividend).length() - Integer.toString(subtractive).length())) {
            shift++;
        }
        return shift;
    }

    /**
     * Определение вычетаемого для текущего шага
     */
    private static int calculateSubtractive(int dividend, int divisor) {
        return divisor * (dividend / divisor);
    }

    /**
     * Определение делимого для следующего шага
     *
     * @param stepDividend - делимое текущего шага
     * @param oldDividend - входное значение делимого текущего шага
     * @param subtractive -  текущее вычетаемое
     * @return Новое делимое
     */
    private static int calculateNewDividend(int stepDividend, int oldDividend, int subtractive) {
        String oldDividendStr = Integer.toString(oldDividend);
        String stepDifferenceStr = Integer.toString(stepDividend - subtractive);
        String newDividendStr = oldDividendStr.replaceFirst(Integer.toString(stepDividend), stepDifferenceStr);

        return Integer.parseInt(newDividendStr);
    }

    /**
     * Создание массива с входными значениями и последующая их проверка
     * @return inputArr[0] - dividend, inputArr[1] - divisor
     */
    private static int[] getValues() {
        int[] inputArr = new int[2];
        Scanner in = new Scanner(System.in);
        do {
            inputArr[0] = inputValue(in, "Input dividend:");
            inputArr[1] = inputValue(in , "Input divisor:");
        } while (!validateInput(inputArr[0],inputArr[1]));
        in.close();
        return inputArr;
    }

    /**
     * Контроль входных данных
     */
    private static boolean validateInput(int dividend, int divisor) {
        boolean correct = true;
        if (dividend < divisor) {
            System.out.println("Error! Dividend must be greater than divisor.");
            return !correct;
        } else if (divisor == 0) {
            System.out.println("Error! Division by zero.");
            return !correct;
        } else return correct;
    }

    /**
     * Считывание целого числа с клавиатуры
     */
    private static int inputValue(Scanner in, String message) {
        System.out.print(message);
        while (!in.hasNextInt()) {
            System.out.println("That's not a number! Try again.");
            in.next();
        }
        return Integer.parseInt(in.next());
    }

    /**
     * Создание массива из числа
     *
     * @param value - Входное целое число
     * @return массив целых
     */
    private static int[] intToArray(int value) {
        int length = Integer.toString(value).length();
        int[] result = new int[length];

        for (int i = length - 1; i >= 0; i--) {
            result[i] = value % BASE;
            value = value / BASE;
        }
        return result;
    }

    /**
     * Создание числа из массива
     *
     * @param array - Массив целых
     * @return целое число
     */
    private static int arrayToInt(int[] array) {
        int value = array[0];
        int length = array.length;

        for (int i = 1; i <= length - 1; i++) {
            value = array[i] + value * BASE;
        }
        return value;
    }

    /**
     * Определение делимого на шаг
     */
    private static int getStepDividendValue(int dividend, int divisor) {
        int index = 0;
        int newDividend;
        int[] dividendArray = intToArray(dividend);

        do {
            index++;
            int[] array = Arrays.copyOfRange(dividendArray, 0, index);
            newDividend = arrayToInt(array);
        } while (divisor > newDividend);
        return newDividend;
    }

    /**
     * Создание строки длинны @param length
     * @see <a href="http://stackoverflow.com/a/13475390">Stackoverflow answer</a>
     * @see <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html#syntax">Format String Syntax</a>
     */
    private static String generateSpaces(int length) {
         return (length == 0) ? "" : String.format("%" + length + "s", " ");
    }

    /**
     * Проверка на знак для результата операции деления
     * @return true если знак есть
     */
    private static boolean checkSign(int value1, int value2) {
        return (value1 < 0 ^ value2 < 0);
    }
}